The use of object-oriented programming techniques can facilitate the development of complex computer programs. Programming languages that support object-oriented techniques have been developed. One such programming language is C++.
Two common characteristics of object-oriented programming languages are support for data encapsulation and data type inheritance. Data encapsulation refers to the binding of functions and data. Inheritance refers to the ability to declare a data type in terms of other data types.
In the C++ language, object-oriented techniques are supported through the use of classes. A class is a user-defined type. A class declaration describes the data members and function members of the class. For example, the following declaration defines data members and a function member of a class named CIRCLE.
______________________________________ class CIRCLE { int x, y; int radius; void draw(); }; ______________________________________
Variables x and y specify the center location of a circle and variable radius specifies the radius of the circle. These variables are referred to as data members of the class CIRCLE. The function draw is a user-defined function that draws the circle of the specified radius at the specified location. The function draw is referred to as a function member of class CIRCLE. The data members and function members of a class are bound together in that the function operates on an instance of the class. An instance of a class is also called an object of the class.
In the syntax of C++, the following statement declares the objects a and b to be of type class CIRCLE.
CIRCLE a, b; PA1 a.draw(); PA1 b.draw(); PA1 class CIRCLE.sub.-- 1: CIRCLE {. . .}; PA1 class CIRCLE.sub.-- 2: CIRCLE {. . .}; PA1 class PATTERN: CIRCLE.sub.-- 1, CIRCLE.sub.-- 2{. . .}; PA1 class CIRCLE.sub.-- 1: virtual CIRCLE {. . .}; PA1 class CIRCLE.sub.-- 2: virtual CIRCLE {. . .}; PA1 class PATTERN: CIRCLE.sub.-- 1, CIRCLE.sub.-- 2{. . .}; PA1 CIRCLE *c.sub.-- ptr; PA1 cptr=&c PA1 CIRCLE a; PA1 CIRCLE.sub.-- FILL b; PA1 a.draw(); PA1 b.draw(); PA1 (1) An object that is created with the new operator is explicitly deallocated using the delete operator. When objects are deallocated using the delete operator, memory is freed for the "ultimately derived object," or the object that is a complete object, not an instance representing a base class. PA1 (2) A local (automatic) object with block scope goes out of scope. PA1 (3) The lifetime of a temporary object ends. PA1 (4) A program ends and global or static objects exist. PA1 (5) The destructor is explicitly called by the programmer.
This declaration causes the allocation of memory for the objects a and b, such an allocation is called an instance of the class. The following statements assign data to the data members of objects a and b.
______________________________________ a.x = 2; a.y = 2; a.radius = 1; b.x = 4; b.y = 5; b.radius = 2; ______________________________________
The following statements are used to draw the circles defined by objects a and b.
A derived class is a class that inherits the characteristics--data members and function members--of its base classes. For example, the following derived class CIRCLE.sub.-- FILL inherits the characteristics of the base class CIRCLE.
______________________________________ class CIRCLE FILL : CIRCLE { int pattern; void fill( ); }; ______________________________________
This declaration specifies that class CIRCLE.sub.-- FILL includes all the data and function members that are in class CIRCLE in addition to the those data and function members introduced in the declaration of class CIRCLE.sub.-- FILL, that is, data member pattern and function member fill. In this example, class CIRCLE.sub.-- FILL would have data members x, y, radius, and pattern and function members draw and fill. Class CIRCLE.sub.-- FILL is said to "inherit" the characteristics of class CIRCLE. A class that inherits the characteristics of another class is a derived class (e.g., CIRCLE.sub.-- FILL). A class that does not inherit the characteristics of another class is a primary class (e.g., CIRCLE). A class whose characteristics are inherited by another class is a base class (e.g., CIRCLE is a base class of CIRCLE.sub.-- FILL). A derived class may inherit the characteristics of several classes, that is, a derived class may have several base classes. This is referred to as multiple inheritance.
A derived class may specify that a base class is to be inherited virtually. Virtual inheritance of a base class means that only one instance of the virtual base class exists in the derived class. For example, the following is an example of a derived class with two nonvirtual base classes.
In this declaration class PATTERN inherits class CIRCLE twice nonvirtually through classes CIRCLE.sub.-- 1 and CIRCLE.sub.-- 2. There are two instances of class CIRCLE in class PATTERN.
The following is an example of a derived class with two virtual base classes.
The derived class PATTERN inherits class CIRCLE twice virtually through classes CIRCLE.sub.-- 1 and CIRCLE.sub.-- 2. Since the class CIRCLE is virtually inherited twice, there is only one object of class CIRCLE in the derived class PATTERN. One skilled in the art would appreciate virtual inheritance can be very useful when the class derivation is more complex.
A class may also specify whether its function members are virtual. Declaring that a function member is virtual means that the function can be overridden by a function of the same name and type in a derived class. In the following example, the function draw is declared to be virtual in classes CIRCLE and CIRCLE.sub.-- FILL.
______________________________________ class CIRCLE { int x, y; int radius; virtual void draw( ); }; class CIRCLE FILL : CIRCLE { int pattern; virtual void draw( ); }; ______________________________________
The C++ language provides pointers data type. A pointer holds values that are addresses of objects in memory. Through a pointer, an object can be referenced. The following statement declares variable c.sub.-- ptr to be a pointer on an object of type class CIRCLE and sets variable c.sub.-- ptr to hold the address of object c.
Continuing with the example, the following statement declares object a to be of type class CIRCLE and object b to be of type class CIRCLE.sub.-- FILL.
The following statement refers to the function draw as defined in class CIRCLE.
Whereas, the following statement refers to the function draw defined in class CIRCLE.sub.-- FILL.
Moreover, the following statements type cast object b to an object of type class CIRCLE and invoke the function draw that is defined in class CIRCLE.sub.-- FILL.
______________________________________ CIRCLE c; CIRCLE *c ptr; c ptr = &b; c ptr-&gt;draw( ); / / CIRCLE FILL::draw( ) ______________________________________
Thus, the virtual function that is called is function CIRCLE.sub.-- FILL::draw.
The C++ language defines several kinds of functions that can be declared only as class members. These functions include constructors, which create objects, and destructors, which destroy objects. These functions can be called explicitly by the program or implicitly by the compiler. Constructors and destructors can be generated automatically by the compiler. In addition, a programmer can specify additional code that is executed by a constructor or destructor.
A constructor for a class is invoked whenever an object for that class is created. A constructor performs various tasks that are associated with building a class object. After memory for an object is allocated, a constructor initializes virtual base and virtual function related data, invokes base class and data member constructors in order of declaration, and executes programmer-specified code. A constructor has the same name as the class itself (e.g., CIRCLE::CIRCLE()).
Destructors perform the inverse function of the constructors. A destructor performs various tasks that are associated with destroying an object. Destructors execute programmer-specified code and invoke the destructors for base classes and data members in reverse order of declaration. After a destructor is executed, the memory for the object can then be deallocated. A destructor has the same name as the class itself, but with a tilde preceding it (e.g., CIRCLE::.about.CIRCLE()). Destructors are called when one of the following events occurs:
If a base class or data member has an accessible destructor, and a derived class does not declare a destructor, the compiler generates one. This compiler-generated destructor invokes destructors for base classes and for data members of the derived class.
Memory that is allocated using the new operator is deallocated using the function operator delete. The compiler declares a global function operator delete. A programmer can declare other global function operator deletes. The programmer can also declare a static member function operator delete for a class. This member function operator delete is called when non-array instances of the class are to be deallocated after they are destroyed by the destructor. The function operator delete deallocates the memory of an object.
The following statements declare classes CIRCLE and CIRCLE.sub.-- FILL with a constructor and destructor for each class. Class CIRCLE.sub.-- FILL inherits class CIRCLE.
______________________________________ class CIRCLE { int x, y; int radius; virtual void draw( ); virtual void erase( ); CIRCLE( ) {x=0; y=0; radius=1}; virtual .about.CIRCLE( ) {this-&gt;erase( )}; class CIRCLE FILL : CIRCLE { int pattern; virtual void draw( ); virtual void erase( ); CIRCLE FILL( ) {pattern=0} virtual .about.CIRCLE FILL( ) {this-&gt;erase( )}; ______________________________________
The constructor and destructor for class CIRCLE are function members CIRCLE and .about.CIRCLE, respectively. Constructor CIRCLE includes programmer-specified code to initialize the data members. When an object of type class CIRCLE is created, the constructor is invoked to initialize virtual function information and to execute programmer-specified code. Destructor .about.CIRCLE includes programmer-specified code, which invokes the function erase that may perform clean-up tasks such as removing the circle from the display. When an object of type class CIRCLE is destroyed, the programmer-specified code is executed.
The constructor and destructor for class CIRCLE.sub.-- FILL are function members CIRCLE.sub.-- FILL and .about.CIRCLE.sub.-- FILL, respectively. Constructor CIRCLE.sub.-- FILL includes programmer-specified code to initialize the data member. When an object of type class CIRCLE.sub.-- FILL is created, the constructor is invoked to invoke the constructor for class CIRCLE, to initialize virtual function data, and to execute programmer-specified code. Destructor .about.CIRCLE.sub.-- FILL includes programmer-specified code that invokes function erase, which may perform clean-up tasks such as removing the circle from the display. When an object of type class CIRCLE.sub.-- FILL is destroyed, the programmer-specified code is executed and the destructor for class CIRCLE is invoked.
FIG. 2 is an overall flow diagram of a destructor generated by a typical prior compiler. The destructor receives three parameters when invoked. The first parameter indicates whether the object being destroyed is for an ultimately derived object. The second parameter indicates whether the destructor is being invoked for a single object or for an array (vector) of objects. The third parameter indicates whether the object memory is to be deallocated. In step 201, the destructor executes any programmer-specified code. Steps 202 through 208 are generated automatically by the compiler as needed. In step 202, the destructor invokes the destructors for the data members. When these data member destructors are invoked, the parameters indicate that the object is not ultimately derived and that memory is not to be deallocated. In step 203, the destructor invokes the destructors for each nonvirtual direct base class in the reverse order in which the base class names are declared. When these base class destructors are invoked, the parameters indicate that the object is not ultimately derived and that memory is not to be deallocated. In step 204, if the destructor is called for an object of an ultimately derived object, then the destructor continues at step 205, else the destructor returns. In step 205, the destructor invokes each of the destructors for virtual base classes in the reverse order of their appearance in a depth-first left-to-right traversal of the inheritance tree (see Detailed Description of the Invention). In step 206, if a vector of objects is indicated and not all of the objects have been processed, then the destructor loops to step 201 to process the next object in the vector, else the destructor continues at step 207. In step 207, if memory is to be deallocated for the object or vector of objects, then the destructor continues at step 208, else the destructor returns. In step 208, the destructor deallocates the memory and returns.
The following statements are used to further illustrate the methods of prior compilers for implementing destructors. The following statements declare classes X, V, A, B, and C.
______________________________________ class X { int mx1; int mx2; virtual .about.X( ){. . .}; }; class V { int mv1; int mv2; virtual .about.V( ){. . .}; }; class A : virtual V { int ma1; int ma2; virtual .about.A( ){. . .}; }; class B : A { int mb1; int mb2; void operator delete (void*); }; class C : B, virtual V { X mc1; int mc2; int mc3; virtual .about.C( ){. . .}; void operator delete (void*); } ______________________________________
FIGS. 3 through 6 are flow diagrams of typical destructors generated by prior compilers for classes. These diagrams correspond to the overall structure of the diagram in FIG. 2.
FIG. 3 is a flow diagram of a destructor for class V. Class V inherits no base classes and has no data members with destructors. Class V has a programmer-declared virtual destructor .about.V. In step 301, the destructor executes the programmer-specified code for destructor .about.V. Steps 302 and 303 are shown in dashed lines to indicate that a .compiler generates no code to invoke destructors for direct nonvirtual base classes and data members because class V has no data members with destructors and no nonvirtual direct base classes. In step 304, if the V object is ultimately derived, then the destructor continues at step 305, else the destructor returns. Step 305 is shown in dashed lines to indicate that the compiler generates no code to invoke destructors for virtual base classes because class V has no virtual base classes. In step 306, if the destructor is invoked for a vector of V objects and not all the objects have been processed, then the destructor loops to step 301 to process the next object, else the destructor continues at step 307. In step 307, if the memory is to be deallocated for the V object or vector of V objects, then the destructor continues at step 308, else the destructor returns. In step 308, the destructor deallocates the memory for the V object or vector of V objects and returns.
Since the declaration of class X is analogous to the declaration of class V, the destructor for class X is also analogous and is not described in detail.
FIG. 4 is a flow diagram of the destructor for class A. Class A virtually inherits class V and has no data members with destructors. Class A has a programmer declared virtual destructor .about.A, which overrides the destructor for class V. In step 401, the destructor executes the programmer specified code for destructor .about.A. Steps 402 and 403 are shown in dashed lines to indicate that a compiler generates no code to invoke nonvirtual base class and data member destructors because class A has no data members with destructors and no nonvirtual direct base classes. In step 404, if the A object is ultimately derived, then the destructor continues at step 405, else the destructor returns. In step 405, the destructor invokes the destructor for the virtual base class V, passing a parameter indicating that it is not being invoked for an ultimately derived V object. In step 406, if the destructor is invoked for a vector of A objects and not all the objects have been processed, then the destructor loops to step 401 to process the next object, else the destructor continues at step 407. In step 407, if the memory is to be deallocated for the A object or vector of A objects, then the destructor continues at step 408, else the destructor returns. In step 408, the destructor deallocates the memory for the A object or vector of A objects and returns.
FIG. 5 is a flow diagram of the destructor for class B. Class B inherits class A, has no data members with destructors, and has no programmer-declared destructor. Even though class B has no programmer-declared destructor, the compiler generates the destructor as shown in FIG. 5. Since a base class has a virtual destructor, the compiler-generated destructor .about.B is also virtual. Steps 501 and 502 are shown in dashed lines to indicate that there is no programmer-specified code to execute, and that there are no data members with destructors. In step 503, the destructor invokes the destructor for the nonvirtual direct base class A, passing a parameter indicating that it is not being invoked for an ultimately derived A object. In step 504, if the B object is ultimately derived, then the destructor continues at step 505, else the destructor returns. In step 505, the destructor invokes the destructor for the virtual base class V, passing a parameter indicating that it is not being invoked for an ultimately derived V object. In step 506, if the destructor is invoked for a vector of B objects and not all the objects have been processed, then the destructor loops to steps 501 to process the next object, else the destructor continues at step 507. In step 507, if the memory is to be deallocated for the B object or vector of B objects, then the destructor continues at step 508, else the destructor returns. In step 508, the destructor deallocates the memory for the B object or vector of B objects and returns.
FIG. 6 is a flow diagram for a destructor of class C. Class C inherits class B and virtually inherits class V, has data member X with a destructor, and has programmer-declared destructor .about.C. In step 601, the destructor executes the programmer-specified code for destructor .about.C. In step 602, the destructor invokes the destructor for class X, passing a parameter indicating that the object is an ultimately derived object, but that memory is not to be deallocated. In step 603, the destructor invokes the destructor for the nonvirtual direct base class B, passing a parameter indicating that it is not being invoked for an ultimately derived X object. In step 604, if the destructor is being invoked for an ultimately derived object, then the destructor continues at step 605, else the destructor returns. In step 605, the destructor invokes the destructor for the virtually inherited base class V, passing a parameter indicating that is not being invoked for an ultimately derived V object. In step 606, if the destructor is invoked for a vector of V objects and not all the objects have been processed, then the destructor loops to steps 601 to process the next object, else the destructor continues at step 607. In step 607, if the memory is to be deallocated for the V object or vector of V objects, then the destructor continues at step 608, else the destructor returns. In step 608, the destructor deallocates the memory for the V object or vector of V objects and returns.